/**
 * Project Name:gxr.common
 * File Name:AbstractRedisCacheService.java
 * Package Name:com.weijie.gxr.common.redis
 * Date:2016年11月6日上午11:05:21
 * Copyright (c) 2016, chenzhou1025@126.com All Rights Reserved.
 *
*/

package com.chuanke.ckfamily.service.common.impl;

import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSizeExpr.Unit;
import com.chuanke.ckfamily.dao.finance.model.Bill;
import com.chuanke.ckfamily.dao.finance.model.GainPacket;
import com.chuanke.ckfamily.dao.finance.model.RedPacket;
import com.chuanke.ckfamily.dao.social.model.Family;
import com.chuanke.ckfamily.service.common.CacheService;
import com.chuanke.ckfamily.service.common.exception.LockException;
import com.chuanke.ckfamily.service.constant.BillType;
import com.chuanke.ckfamily.service.constant.RedPacketType;
import com.weijie.core.util.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.security.Key;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * ClassName:AbstractRedisCacheService <br/>
 * Function: TODO ADD FUNCTION. <br/>
 * Reason:	 TODO ADD REASON. <br/>
 * Date:     2016年11月6日 上午11:05:21 <br/>
 * @author   Administrator
 * @version  
 * @param <V>
 * @param <K>
 * @since    JDK 1.7
 * @see 	 
 */
public abstract class AbstractRedisCacheService<K, V> implements CacheService<K,V> {

	private static final long serialVersionUID = -4429492892424335255L;

	private static final Logger logger = LoggerFactory.getLogger(AbstractRedisCacheService.class);

	@Autowired  
	 protected RedisTemplate<K, V> redisTemplate;  

	public Boolean keyExists(K key) {
		long start = System.currentTimeMillis();
		boolean bol = redisTemplate.hasKey(key); 
		long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.keyExists执行时间超过"+ms+"ms");
		}
	    return bol;
	}
	
	@Override
	public Boolean keyExists(K key, V value) {
		long start = System.currentTimeMillis();
		boolean result = false;
		if(redisTemplate.hasKey(key)){
			if(value.equals(get(key))){
				result = true;
			}
		}
		long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.keyExists执行时间超过"+ms+"ms");
		}
		return result;
	}

	@Override
	public V get(K key) {
//		logger.debug("CacheService.get(K) "+key);
		long start = System.currentTimeMillis();
		BoundValueOperations<K, V> valueOper = redisTemplate.boundValueOps(key);
		V v = valueOper.get();
		long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.get(K)执行时间超过"+ms+"ms");
		}
	    return v;
	}

	@Override
	public void set(K key, V value) {
		long start = System.currentTimeMillis();
		if(StringUtils.isEmpty(value)){
			logger.debug("尝试写入的value为空, k="+key +" v="+value);
			return;
		}
	
		BoundValueOperations<K, V> valueOper = redisTemplate.boundValueOps(key);
		valueOper.set(value);
		long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.set(k v)执行时间超过"+ms+"ms");
		}
	}

	@Override
	public void set(K key, V value, long expiredTime) {
		long start = System.currentTimeMillis();
		if(StringUtils.isEmpty(value)){
			logger.debug("尝试写入的value为空, k="+key +" v="+value);
			return;
		}
		if(expiredTime == 0){
			logger.debug("尝试写入的value,设置了过期时间为0, k="+key +" v="+value);
			return;
		}
		BoundValueOperations<K, V> valueOper = redisTemplate.boundValueOps(key);
	    if (expiredTime >= 0) {  
	    	valueOper.set(value, expiredTime, TimeUnit.MILLISECONDS);  
	    }
	    long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.set(k v time)执行时间超过"+ms+"ms");
		}
	}

	@Override
	public void delete(K key) {
		long start = System.currentTimeMillis();
		if (redisTemplate.hasKey(key)) {
            redisTemplate.delete(key); 
            logger.debug("CacheService.delete删除 "+key+"");
        }  
		long end = System.currentTimeMillis();
		long ms = end - start;
		if(ms > 100){
			logger.warn("CacheService.delete执行时间超过"+ms+"ms");
		}
	}

	@Override
	public RedisLockUtil getLock(String lockKey) throws LockException {
		RedisLockUtil lock = new RedisLockUtil(redisTemplate, lockKey);
		if (lock.lock()) {
			return lock;

		} else {
			throw new LockException("获取分布式锁失败！");

		}
	}
	
	@Override
	public  void geoAdd(K key, String lng, String lat, V value) {
		logger.debug("geoAdd==================>lng={},lat={}, value={}",lng, lat, value);
		BoundGeoOperations<K, V> boundGeoOps = redisTemplate.boundGeoOps(key);
		Point point = new Point(Double.parseDouble(lng), Double.parseDouble(lat));
		boundGeoOps.geoAdd(point, value);
	}
	@Override
	public void geoRemove(K key, V... value) {
		BoundGeoOperations<K, V> boundGeoOps = redisTemplate.boundGeoOps(key);
		boundGeoOps.geoRemove(value);
	}
	
	@Override
	public List<GeoResult<GeoLocation<V>>>  geoRadius(K key, String lng, String lat, Double radius) {
		radius = radius == null ? 99999999 : radius;
		BoundGeoOperations<K, V> boundGeoOps = redisTemplate.boundGeoOps(key);
		Point point =new Point(Double.parseDouble(lng), Double.parseDouble(lat));
		Distance distance = new Distance(radius, Metrics.KILOMETERS);
		Circle circle=new org.springframework.data.geo.Circle(point, distance);
		GeoRadiusCommandArgs newGeoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
		newGeoRadiusArgs.includeCoordinates();
		newGeoRadiusArgs.includeDistance();
		newGeoRadiusArgs.sortAscending();

		GeoResults<GeoLocation<V>> geoRadius = boundGeoOps.geoRadius(circle, newGeoRadiusArgs);
		return geoRadius.getContent();
	}

	@Override
	public List<GeoResult<GeoLocation<V>>> geoRadiusByMember(K key, V value, Double radius) {
		radius=radius==null?99999999:radius;
		BoundGeoOperations<K, V> boundGeoOps = redisTemplate.boundGeoOps(key);
		Distance distance = new Distance(radius, Metrics.KILOMETERS);
		GeoRadiusCommandArgs newGeoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
		newGeoRadiusArgs.includeCoordinates();
		newGeoRadiusArgs.includeDistance();
		newGeoRadiusArgs.sortAscending();
		GeoResults<GeoLocation<V>> geoRadiusByMember = boundGeoOps.geoRadiusByMember(value, distance, newGeoRadiusArgs);
		List<GeoResult<GeoLocation<V>>> content = geoRadiusByMember.getContent();
		return content;
	}

	public Long listSize(K key) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		return boundListOps.size();
	}

	public List<V> getLists(K key, Long begin, Long end) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		return boundListOps.range(begin, end);
	}

	@Override
	public V rightPop(K key) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		return boundListOps.rightPop();
	}
	
	@Override
	public V rightPop(K key, long timeout) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		return boundListOps.rightPop(timeout,  TimeUnit.MILLISECONDS);
	}

	@Override
	public V index(K key, int index) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		return boundListOps.index(index);
	}

	@Override
	public void leftPush(K key, V value) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		boundListOps.leftPush(value);
	}

	@Override
	public void leftPushAll(K key, V... values) {
		BoundListOperations<K, V> boundListOps = redisTemplate.boundListOps(key);
		boundListOps.leftPushAll(values);
	}
	@Override
	public void flushDb() {
		   redisTemplate.execute(new RedisCallback<Boolean>() {
	            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
	                connection.flushDb();
	                return true;
	            }
	        });
	}

	@Override
	public void invokeSynzied(K key, long timeOut, SynizedInvoker invoker) {

	}
}